﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Collections;
using System.Threading;

using log4net;
using RollBackLib.src.utils;
using System.Net.Mail;
using RollBack.src.ui.Dialogs;
using RollBackLib.src.core;
using RollbackLib.src.utils;


namespace RollBack.src.ui
{
    /// <summary>
    /// Encapsulates the bottom panel containing the various backup set tabs, toolbar and the destination panel.
    /// </summary>
    public partial class BackupSetComposite : UserControl, IDisposable
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(BackupSetComposite));
        public BackupRestoreRunner m_backupRunner;
        BackupJobTabEventHandlers m_jobTabEventHandlers;

        public BackupSetComposite( )
        {
            InitializeComponent();
            {
                this.m_backupSetTabs.ImageList = ImageResources.m_resources.m_imageList;
            }
            {
                //m_backupSetTab.DrawMode = TabDrawMode.OwnerDrawFixed;
                //m_backupSetTab.DrawItem += new DrawItemEventHandler(m_backupSetTab_DrawItem);

            }
        }

        public void setEventHandler(BackupJobTabEventHandlers handlers)
        {
            m_jobTabEventHandlers = handlers;
        }

        #region Code for owner drawn tab with specialized background.  Currently not used
        /// <summary>
        /// Color the tab items differently
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void m_backupSetTab_DrawItem(object sender, DrawItemEventArgs e)
        {
            TabPage CurrentTab = m_backupSetTabs.TabPages[e.Index];
            Rectangle ItemRect = m_backupSetTabs.GetTabRect(e.Index);
            SolidBrush FillBrush = new SolidBrush(System.Drawing.SystemColors.MenuBar);
            SolidBrush TextBrush = new SolidBrush(System.Drawing.SystemColors.ControlText);
            StringFormat sf = new StringFormat();
            Font font = new Font(e.Font, FontStyle.Regular);
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;

            //If we are currently painting the Selected TabItem we'll
            //change the brush colors and inflate the rectangle.
            bool colorSelected = false;
            BackupJobTab jobTab = (BackupJobTab)CurrentTab; // getSelectedBackupJob();
            if (jobTab!=null) {
                BackupInfo.RunStatus rstatus = jobTab.getBackupJob().m_RunningStatus;
                bool running =
                    (rstatus == BackupInfo.RunStatus.Running || rstatus == BackupInfo.RunStatus.Paused);

                if (running)
                {
                    colorSelected = true;
                    FillBrush.Color = Color.Green;
                    TextBrush.Color = Color.White;
                    font = new Font(e.Font, FontStyle.Bold);
                }
            }
            if (System.Convert.ToBoolean(e.State & DrawItemState.Selected) )
            {
                if (!colorSelected)
                {
                    ItemRect.Inflate(2, 2);
                    FillBrush.Color = System.Drawing.SystemColors.ActiveCaption;
                }
            }

            //Set up rotation for left and right aligned tabs
            if (m_backupSetTabs.Alignment == TabAlignment.Left || m_backupSetTabs.Alignment == TabAlignment.Right)
            {
                float RotateAngle = 90;
                if (m_backupSetTabs.Alignment == TabAlignment.Left)
                    RotateAngle = 270;
                PointF cp = new PointF(ItemRect.Left + (ItemRect.Width / 2), ItemRect.Top + (ItemRect.Height / 2));
                e.Graphics.TranslateTransform(cp.X, cp.Y);
                e.Graphics.RotateTransform(RotateAngle);
                ItemRect = new Rectangle(-(ItemRect.Height / 2), -(ItemRect.Width / 2), ItemRect.Height, ItemRect.Width);
            }

            //Next we'll paint the TabItem with our Fill Brush
            e.Graphics.FillRectangle(FillBrush, ItemRect);

            //Now draw the text.
            e.Graphics.DrawString(CurrentTab.Text, font, TextBrush, (RectangleF)ItemRect, sf);

            //Reset any Graphics rotation
            e.Graphics.ResetTransform();

            font.Dispose();
            //Finally, we should Dispose of our brushes.
            FillBrush.Dispose();
            TextBrush.Dispose();
        }
        #endregion

        public void removeBackupJobTab(BackupJobTab tab)
        {
            m_backupSetTabs.Controls.Remove(tab);
        }

        /// <summary>
        /// Add a new BackupJob tab
        /// </summary>
        /// <param name="job"></param>
        public BackupJobTab addNewBackupJobTab(BackupJob job)
        {
            BackupJobTab tab = new BackupJobTab();
            addNewBackupJobTab(tab,job);
            return tab;
        }

        /// <summary>
        /// Add a backup tab to the backup tab control
        /// </summary>
        /// <param name="tab"></param>
        private void addNewBackupJobTab(BackupJobTab tab,BackupJob job)
        {
            tab.setJob(job);
            tab.m_BackupDestPanel.setButtonHandlers(
                new System.EventHandler(m_jobTabEventHandlers.m_startButtonClickHandler),
                new System.EventHandler(m_jobTabEventHandlers.m_pauseButtonClickHandler),
                new System.EventHandler(m_jobTabEventHandlers.m_advancedButtonClickHandler));
            tab.m_FileList.m_fileListView.DragDrop += new DragEventHandler(m_FileList_DragDrop);
            this.m_backupSetTabs.Controls.Add(tab);
            tab.updateToolTip("Not Running");
        }

        void m_FileList_DragDrop(object sender, DragEventArgs e)
        {
            if (m_jobTabEventHandlers.m_dragDropHandler != null)
            {
                m_jobTabEventHandlers.m_dragDropHandler(sender, e);
            }
        }

        /// <summary>
        /// Return the currently selected backup tab job.
        /// </summary>
        /// <returns></returns>
        public BackupJobTab getSelectedBackupTab()
        {
            BackupJobTab tab = null;
            if (m_backupSetTabs.SelectedIndex >= 0 && m_backupSetTabs.TabPages.Count>0)
            {
                tab = (BackupJobTab)m_backupSetTabs.SelectedTab;
            }
            return tab;
        }

        public BackupJob getSelectedBackupJob()
        {
            BackupJobTab tab = getSelectedBackupTab();
            BackupJob job = tab != null ? tab.getBackupJob() : null;
            return job;
        }


        /// <summary>
        /// Add a callback when the "backup job" tab selection changes
        /// </summary>
        internal void setJobTabSelectionHandler(TabControlEventHandler func)
        {
            this.m_backupSetTabs.Selected += new System.Windows.Forms.TabControlEventHandler(func);
        }

        /// <summary>
        /// Enable the panel on the left bottom which contains the list of files/directories to be backed up.
        /// </summary>
        internal void switchtoBackupListView()
        {
            log.Debug("Switching all tabs in BackupSetTabs to use the internal backup list.");
            foreach (TabPage page in m_backupSetTabs.TabPages)
            {
                if (page is BackupJobTab)
                {
                    BackupJobTab jobTab = (BackupJobTab)page;
                    jobTab.m_FileList.switchToBackupList();
                }
            }
        }

        /// <summary>
        /// Disable the panel on the left bottom which contains the list of files/directories to be backed up.
        /// </summary>
        internal void switchToRestoreListView()
        {
            log.Debug("Switching all tabs in BackupSetTabs to use the internal restore list.");
            foreach (TabPage page in m_backupSetTabs.TabPages)
            {
                if(page is BackupJobTab) 
                {
                    BackupJobTab jobTab = (BackupJobTab)page;
                    jobTab.m_FileList.switchToRestoreList();
                }
            }
        }

        /// <summary>
        /// Handle start button click via a callback
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void startButtonClick(BackupInfo bInfo)
        {
            m_backupRunner.startBackup(bInfo);
        }

        public void stopButtonClick( )
        {
            m_backupRunner.stopCurrentBackup();
        }

        public void pauseButtonClick( )
        {
            BackupJobTab tab = getSelectedBackupTab();
            if (tab != null)
            {
                m_backupRunner.pauseResume();
            }
        }

        /// <summary>
        /// Handle the click on the Advanced button
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void advancedButtonClick(object sender, System.EventArgs e)
        {
            BackupJobTab tab = getSelectedBackupTab();
            if (tab != null)
            {
                AdvancedDialogForm form = new AdvancedDialogForm();
                JobSchedule schedule = tab.getBackupJob().m_JobSchedule;
                form.updateForm(tab,tab.getBackupJob(),false);
                System.Windows.Forms.DialogResult result = form.ShowDialog();
                if (result == System.Windows.Forms.DialogResult.OK)
                {
                    form.updateJob( );
                }
            }
        }

        /// <summary>
        /// Return true if any of the backup jobs have been modified since the last time the backup job was saved.
        /// </summary>
        /// <returns></returns>
        internal bool haveBackupJobsChanged()
        {
            bool changed = false;
            lock (m_backupRunner.m_JobManager.m_BackupJobList)
            {
                foreach (BackupJobTab tab in m_backupSetTabs.TabPages)
                {
                    BackupJob job = tab.getBackupJob();
                    if (job != null)
                    {
                        if (job.m_Modified)
                        {
                            changed = true;
                            break;
                        }
                    }
                }
            }
            return changed;
        }

        /// <summary>
        /// Update the contents of the backup job from the UI and then save them
        /// </summary>
        internal void updateAndSaveAllBackupJobs()
        {
            lock (m_backupRunner.m_JobManager.m_BackupJobList)
            {
                log.Info("Starting update of all backup jobs");
                List<BackupJob> l = new List<BackupJob>();
                foreach (BackupJobTab tab in m_backupSetTabs.TabPages)
                {
                    BackupJob job = tab.getBackupJob();
                    if (job != null)
                    {
                        job.updateBackupPathList(toStringList(tab.m_FileList.m_backupList));
                        job.m_RestorePathList = toStringList(tab.m_FileList.m_restoreList);
                        l.Add(job);
                    }
                }

                if (m_backupRunner != null &&
                    m_backupRunner.m_JobManager != null &&
                    m_backupRunner.m_JobManager.m_RootPath != null)
                {
                    XMLUtils.SaveObjectToXML(m_backupRunner.m_JobManager.m_RootPath, l);
                    foreach (BackupJob job in l)
                    {
                        job.m_Modified = false;
                    }
                    log.Info("Finished updating and saving " + l.Count + " backup jobs");
                }
                else
                {
                    log.Warn("Did not save backup jobs since RootPath is null");
                }
            }
        }

        private List<BackupItem> toStringList(List<ListViewItem> inputList)
        {
            List<BackupItem> l = new List<BackupItem>();
            foreach (ListViewItem item in inputList)
            {
                BackupItem b = new BackupItem(item.Text,item.Checked);
                l.Add(b);
            }
            return l;
        }


        internal void shutdown()
        {
            // stop all threads
            m_backupRunner.stopAllThreads();
            updateAndSaveAllBackupJobs();
        }


        /// <summary>
        /// Load BackupJobs from 'file' and return the number of jobs loaded.
        /// </summary>
        /// <param name="file"></param>
        /// <returns></returns>
        internal int loadSavedBackupSets(string file)
        {
            int count = 0;
            AppUI.showStatusMessage("Loading job file <" + file + ">");
            JobManager.BackupDelegate handle = delegate(JobManager.DResult dresult, int param1, object data)
            {
                int ret = 0;
                switch (dresult)
                {
                    case JobManager.DResult.GotException:
                        {
                            Exception e = (Exception)data;
                            log.Warn("Caught an exception when trying to load file <" + file + ">", e);
                            AppUI.ShowException("Application will continue without loading previous backup jobs.\r\nCaught an exception when trying to load file <" + file + ">", e);
                            ret = 1;
                        }
                        break;
                    case JobManager.DResult.LogMsg:
                        {
                            log.Info((string)data);
                            ret = 0;
                        }
                        break;
                }
                return ret;
            };

            lock (m_backupRunner.m_JobManager)
            {
                JobManager jobManager = m_backupRunner.m_JobManager;
                
                jobManager.LoadSavedBackupSets(file, handle);
                foreach (BackupJob job in jobManager.m_BackupJobList)
                {
                    try
                    {
                        BackupJobTab tab = addNewBackupJobTab(job);
                        job.setData(tab);
                        ++count;
                    }
                    catch (Exception e)
                    {
                        log.Warn("Caught an exception when trying to load file <" + file + ">", e);
                        AppUI.ShowException("Caught an exception when trying to load file <" + file + ">", e);
                    }
                }
            }
            if (count == 0)
            {
            }
            return count;
        }

        /// <summary>
        /// Create a new backup job.
        /// </summary>
        internal void newBackupJob()
        {
            try
            {
                BackupJob job = new BackupJob();
                job.m_DestDir = (AppUtils.getDefaultBackupLocation(Application.UserAppDataRegistry));
                BackupJobTab tab = new BackupJobTab();
                AdvancedDialogForm form = new AdvancedDialogForm();
                form.updateForm(tab, job,true);
                tab.setJob(job);
                try
                {
                    if (DialogResult.OK == form.ShowDialog())
                    {
                        m_backupRunner.m_JobManager.AddBackupJob(job, tab);
                        addNewBackupJobTab(tab, job);
                        job.setData(tab);
                        tab.Text = job.m_Id.m_Name;
                        AppUI.showStatusMessage("Added new backup job <" + job.m_Id.m_Name + ">");
                    }
                }
                catch (BackupException e)
                {
                    string msg = "Could not create BackupJob";
                    log.Warn(msg, e);
                    AppUI.ShowErrorMessage(msg + "\r\n" + e.Message);
                }
            }
            catch (BackupException e)
            {
                string msg = "Cannot create new Backup job";
                log.Warn(msg, e);
                AppUI.ShowException(msg,e);
            }
        }
    }
}
